package org.bjf.utils;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;

import java.math.BigInteger;

/**
 * @author binjinfeng
 */
public class CodeUtil {
    /**
     * 36位完整字符[0-9][a-z]
     */
    private static char[] FULL_CHAR = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            .toCharArray();
    /**
     * 34位易于识别的字符（去掉I和O）
     */
    private static char[] CLEAR_CHAR = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ"
            .toCharArray();

    public static String genClearCode() {
        //===1.生成唯一ID（整数10进制）
        String idStr = IdWorker.getIdStr();
        BigInteger number = new BigInteger(idStr);

        //===2.进制转换（转成34进制）
        StringBuilder sb = new StringBuilder();
        BigInteger toRadix = new BigInteger("34");
        while (number.compareTo(BigInteger.ZERO) != 0) {
            BigInteger mod = number.mod(toRadix);
            sb.append(CLEAR_CHAR[mod.intValue()]);
            number = number.divide(toRadix);
        }
        String realCode = sb.reverse().toString();

        /**
         * 斐波那契（Fibonacci）散列法
         *
         * 1，对于16位整数而言，这个乘数是40503 
         * 2，对于32位整数而言，这个乘数是2654435769 
         * 3，对于64位整数而言，这个乘数是11400714819323198485
         * ————————————————
         * 对我们常见的32位整数而言，公式：
         *    index = (value * 2654435769) >> 28
         *    Math.abs(index)  取绝对值，避免溢出为负数
         */
        //===3.生成2位校验码
        String md5 = DigestUtils.md5Hex(realCode);
        long h1 = (StringUtils.substring(md5, 1, 6).hashCode() * 2654435769L) >> 28;
        long h2 = (StringUtils.substring(md5, 3, 7).hashCode() * 2654435769L) >> 28;
        long v1 = Math.abs(h1) % 34;
        long v2 = Math.abs(h2) % 34;
        String code = realCode + CLEAR_CHAR[(int) v1] + CLEAR_CHAR[(int) v2];


        return code.toUpperCase();
    }

    public static boolean validCode(String code) {
        String realCode = StringUtils.substring(code, 0, code.length() - 2);
        String md5 = DigestUtils.md5Hex(realCode);
        long h1 = (StringUtils.substring(md5, 1, 6).hashCode() * 2654435769L) >> 28;
        long h2 = (StringUtils.substring(md5, 3, 7).hashCode() * 2654435769L) >> 28;
        long v1 = Math.abs(h1) % 34;
        long v2 = Math.abs(h2) % 34;
        String serverCode = realCode + CLEAR_CHAR[(int) v1] + CLEAR_CHAR[(int) v2];
        if (serverCode.toUpperCase().equals(code)) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 300; i++) {
            System.out.println(genClearCode());
        }
        System.out.println("duration = " + (System.currentTimeMillis() - start));

        boolean r = validCode("H25QV2NMLYUS8B");
        System.out.println("r = " + r);
    }


}